<template>
  <view class="calc-page" :class="{ 'modal-open': showResultsModal }">
    <!-- 顶部标题 -->
    <view class="calc-hero">
      <text class="calc-title">退休计算器</text><br>
      <text class="calc-sub">通过精确计算，助力您规划从容退休生活~</text>
     </view>

    <!-- 基础信息输入板块 -->
    <view class="card">
      <text class="section-title">基础信息</text>
      <view class="row">
        <text class="label">📅 当前年龄(岁)</text>
         <input class="input" :class="{ invalid: isInvalid('currentAge') }" type="number" v-model.number="form.currentAge" placeholder="0-100" />
       </view>
       <view class="hint" v-if="isInvalid('currentAge')"><text class="error-hint">{{ getErrorHint('currentAge') }}</text></view>
       <view class="row">
        <text class="label">🎯 退休年龄(岁)</text>
         <input class="input" :class="{ invalid: isInvalid('retireAge') }" type="number" v-model.number="form.retireAge" placeholder="0-100" />
       </view>
       <view class="hint" v-if="isInvalid('retireAge')"><text class="error-hint">{{ getErrorHint('retireAge') }}</text></view>
       <view class="row">
       <text class="label">🏦 当前储蓄</text>
         <input class="input" :class="{ invalid: isInvalid('currentSavings') }" type="digit" v-model.number="form.currentSavings" placeholder=">=0" />
       </view>
       <view class="hint" v-if="isInvalid('currentSavings')"><text class="error-hint">{{ getErrorHint('currentSavings') }}</text></view>
       <view class="row">
        
        <text class="label">💰 每年新增储蓄</text>
         <input class="input" :class="{ invalid: isInvalid('annualContribution') }" type="digit" v-model.number="form.annualContribution" placeholder=">=0(年末投入)" />
       </view>
       <view class="hint" v-if="isInvalid('annualContribution')"><text class="error-hint">{{ getErrorHint('annualContribution') }}</text></view>
       <view class="row">
        <text class="label">📈 投资年收益率(%)</text>
        <input class="input" :class="{ invalid: isInvalid('returnRate') }" type="digit" v-model.number="form.returnRate" @blur="roundPercent('returnRate',0.1)" placeholder="0-50，步长0.1" />
       </view>
       <view class="hint" v-if="isInvalid('returnRate')"><text class="error-hint">{{ getErrorHint('returnRate') }}</text></view>
       <view class="row">
        <text class="label">⏳ 退休后预期寿命(岁)</text>
        <input class="input" :class="{ invalid: isInvalid('lifespan') }" type="number" v-model.number="form.lifespan" placeholder="0-120" />
       </view>
       <view class="hint" v-if="isInvalid('lifespan')"><text class="error-hint">{{ getErrorHint('lifespan') }}</text></view>
   </view>

    <!-- 通货膨胀板块 -->
    <view class="card">
      <view class="row">
        <text class="label">💹 是否考虑通货膨胀</text>
         <switch :checked="form.considerInflation" @change="form.considerInflation = $event.detail.value" />
       </view>
       <view class="row">
        <text class="label">📊 通货膨胀率(%)</text>
        <input class="input" :class="{ invalid: isInvalid('inflationRate') }" type="digit" v-model.number="form.inflationRate" @blur="roundPercent('inflationRate',0.1)" :disabled="!form.considerInflation" placeholder="0-20，步长0.1" />
       </view>
       <view class="hint" v-if="isInvalid('inflationRate')"><text class="error-hint">{{ getErrorHint('inflationRate') }}</text></view>
    </view>

    <!-- 蒙特卡洛板块 -->
    <view class="card">
      <view class="row">
        <text class="label">🔀 是否使用蒙特卡洛模拟</text>
        <switch :checked="form.useMonteCarlo" @change="form.useMonteCarlo = $event.detail.value" />
      </view>
      <view class="row">
        <text class="label">📉 投资收益率标准差(%)</text>
        <input class="input" :class="{ invalid: isInvalid('returnStd') }" type="digit" v-model.number="form.returnStd" @blur="roundPercent('returnStd',0.1)" :disabled="!form.useMonteCarlo" placeholder="0-20，步长0.1" />
      </view>
      <view class="hint" v-if="isInvalid('returnStd')"><text class="error-hint">{{ getErrorHint('returnStd') }}</text></view>
      <view class="row">
        <text class="label">🔢 模拟次数</text>
        <input class="input" :class="{ invalid: isInvalid('simTimes') }" type="number" v-model.number="form.simTimes" :disabled="!form.useMonteCarlo" placeholder="1000-100000(默认10000)" />
      </view>
      <view class="hint" v-if="isInvalid('simTimes')"><text class="error-hint">{{ getErrorHint('simTimes') }}</text></view>
      </view>

    <!-- 页面动作：计算并弹出结果 -->
    <view class="actions">
      <button class="primary" @click="calculate">计算</button>
       <view class="actions-inline">
         <button class="secondary" @click="clearForm">清空</button>
          <button class="secondary" @click="openHistory">历史记录</button>
       </view>
    </view>
    <!-- 结果弹窗 -->
    <view class="modal-overlay" v-if="showResultsModal" @click.self="closeResults" :catchtouchmove="true" @touchmove.stop.prevent>
      <view class="modal result-modal">
        <view class="modal-header">
          <text class="section-title">计算结果</text>
          <text class="modal-close" @click="closeResults">✖</text>
        </view>
        <view class="row">
          <text class="label">退休时积累金额</text>
          <text class="value">{{ formatCurrency(results.nestEgg) }}</text>
        </view>
        <view class="row">
          <text class="label">退休后每年可取出金额</text>
          <text class="value">{{ formatCurrency(results.withdrawal) }}</text>
        </view>
        <view class="row" v-if="form.useMonteCarlo">
          <text class="label">模拟中位数(年可取出)</text>
          <text class="value">{{ formatCurrency(results.mcMedian) }}</text>
        </view>
        <!-- 添加图表容器 -->
        <view class="row" v-if="form.useMonteCarlo" style="flex-direction: column;">
          <text class="label">模拟结果分布图</text>
          <view class="chart-container">
            <l-echart ref="chartRef" v-if="chartData.length > 0"></l-echart>
          </view>
        </view>
      </view>
    </view>
    
   </view>  
</template>

<script>
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';

export default {
  data() {
    return {
      form: {
        currentAge: 0,
        retireAge: 0,
        currentSavings: 0,
        annualContribution: 0,
        returnRate: 0.0, // %
        lifespan: 0,
        considerInflation: false,
        inflationRate: 0.0, // %
        useMonteCarlo: false,
        returnStd: 0.0, // %
        simTimes: 10000
      },
      results: {
        nestEgg: 0,
        withdrawal: 0,
        mcMedian: 0
      },
      showResultsModal: false,
      chartData: [],
      chartRef: null
    }
  },
  methods: {
     closeResults() {
       this.showResultsModal = false
     },
    navigateBack() {
      uni.navigateBack({ delta: 1 })
    },
    showHelp() {
      uni.showModal({
        title: '说明',
        content: '积累：终值公式(年末投入)；取款：年金公式；真实收益率：((1+名义)/(1+通胀)-1)。可选择蒙特卡洛模拟查看不确定性。',
        showCancel: false
      })
    },
    roundPercent(key, step) {
      const v = Number(this.form[key]) || 0
      const rounded = Math.round(v / step) * step
      this.form[key] = Number(rounded.toFixed(2))
    },
    clampForm() {
      const f = this.form
      f.currentAge = this.clampNum(f.currentAge, 0, 100)
      f.retireAge = this.clampNum(f.retireAge, 0, 100)
      f.currentSavings = Math.max(0, Number(f.currentSavings) || 0)
      f.annualContribution = Math.max(0, Number(f.annualContribution) || 0)
      f.returnRate = this.clampNum(f.returnRate, 0, 50)
      f.lifespan = this.clampNum(f.lifespan, 0, 120)
      f.inflationRate = this.clampNum(f.inflationRate, 0, 20)
      f.returnStd = this.clampNum(f.returnStd, 0, 20)
      f.simTimes = this.clampNum(f.simTimes || 10000, 1000, 100000)
    },
    clampNum(n, min, max) {
      n = Number(n)
      if (isNaN(n)) n = min
      return Math.min(max, Math.max(min, n))
    },
    realRate(nominal, inflation) {
      return (1 + nominal) / (1 + inflation) - 1
    },
    fvWithContrib(pv, r, n, pmtEnd) {
      if (n <= 0) return pv
      if (Math.abs(r) < 1e-9) return pv + pmtEnd * n
      return pv * Math.pow(1 + r, n) + pmtEnd * (Math.pow(1 + r, n) - 1) / r
    },
    annuityWithdrawal(pv, r, n) {
      if (n <= 0) return pv
      if (Math.abs(r) < 1e-9) return pv / n
      return pv * r / (1 - Math.pow(1 + r, -n))
    },
    normalSample(mean, std) {
      // Box-Muller
      const u1 = Math.random()
      const u2 = Math.random()
      const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)
      return mean + std * z
    },
    calculate() {
      this.clampForm()
      const f = this.form
      const n = Math.max(0, f.retireAge - f.currentAge)  // 工作年数
      const m = Math.max(1, f.lifespan - f.retireAge)    // 退休后年数
      
      // 名义收益率和通胀率转为小数形式
      const nominalRate = (Number(f.returnRate) || 0) / 100
      const inflationRate = f.considerInflation ? (Number(f.inflationRate) || 0) / 100 : 0
      
      // 计算实际收益率 Rreal = (1+r) / (1+i) - 1
      const realRate = (1 + nominalRate) / (1 + inflationRate) - 1
      
      // 当前储蓄、每年新增储蓄
      const P = Number(f.currentSavings) || 0
      const A = Number(f.annualContribution) || 0
      const r = realRate  // 实际收益率用于FV计算
      
      // 退休时积累金额 FV = P * (1+r)^n + A * ((1+r)^n - 1) / r
      let FV
      if (Math.abs(r) < 1e-9) {
        // 当利率接近0时，使用线性计算避免除零错误
        FV = P + A * n
      } else {
        FV = P * Math.pow(1 + r, n) + A * (Math.pow(1 + r, n) - 1) / r
      }
      
      // 退休后每年可取金额 W = FV * (rReal * (1+rReal)^m) / ((1+rReal)^m - 1)
      let W
      if (Math.abs(r) < 1e-9) {
        // 当实际利率接近0时，使用线性计算避免除零错误
        W = FV / m
      } else {
        W = FV * (r * Math.pow(1 + r, m)) / (Math.pow(1 + r, m) - 1)
      }

      this.results.nestEgg = FV
      this.results.withdrawal = W

      if (f.useMonteCarlo) {
        const stdNominal = (Number(f.returnStd) || 0) / 100
        const samples = []
        for (let i = 0; i < f.simTimes; i++) {
          // Simulate pre-retirement accumulation with yearly random returns
          let balance = Number(f.currentSavings) || 0
          for (let y = 0; y < n; y++) {
            const yNom = this.normalSample(nominalRate, stdNominal)
            const yInflation = f.considerInflation ? inflationRate : 0
            const yReal = (1 + yNom) / (1 + yInflation) - 1
            balance = balance * (1 + yReal) + (Number(f.annualContribution) || 0)
          }
          // Post-retirement constant withdrawal computed using mean real rate for simplicity
          const finalRealRate = (1 + nominalRate) / (1 + inflationRate) - 1
          const w = this.annuityWithdrawal(balance, finalRealRate, m)
          samples.push(w)
        }
        samples.sort((a, b) => a - b)
        const mid = samples[Math.floor(samples.length / 2)]
        this.results.mcMedian = mid
        
        // 准备图表数据
        this.prepareChartData(samples)
      } else {
        this.results.mcMedian = 0
        this.chartData = []
      }
       // 保存历史记录
       try {
         const rec = {
           timestamp: Date.now(),
           input: {
             currentAge: f.currentAge,
             retireAge: f.retireAge,
             lifespan: f.lifespan,
             currentSavings: P,
             annualContribution: A,
             returnRate: Number(f.returnRate) || 0,
             considerInflation: !!f.considerInflation,
             inflationRate: Number(f.inflationRate) || 0,
             useMonteCarlo: !!f.useMonteCarlo,
             returnStd: Number(f.returnStd) || 0,
             simTimes: Number(f.simTimes) || 0
           },
           output: {
             nestEgg: this.results.nestEgg,
             withdrawal: this.results.withdrawal,
             mcMedian: this.results.mcMedian
           }
         }
         const list = uni.getStorageSync('calcHistory') || []
         if (Array.isArray(list)) {
           list.unshift(rec)
           if (list.length > 100) list.length = 100
           uni.setStorageSync('calcHistory', list)
         } else {
           uni.setStorageSync('calcHistory', [rec])
         }
       } catch (e) {}
       this.showResultsModal = true
      uni.showToast({ title: '计算完成', icon: 'none' })
    },
    prepareChartData(samples) {
      // 创建直方图数据
      const numBins = 20
      const minValue = Math.min(...samples)
      const maxValue = Math.max(...samples)
      const binWidth = (maxValue - minValue) / numBins
      
      const bins = Array(numBins).fill(0)
      const binLabels = []
      
      // 初始化bin标签
      for (let i = 0; i < numBins; i++) {
        const binCenter = minValue + (i + 0.5) * binWidth
        binLabels.push(binCenter)
      }
      
      // 分配样本到bins
      samples.forEach(sample => {
        const binIndex = Math.min(Math.floor((sample - minValue) / binWidth), numBins - 1)
        bins[binIndex]++
      })
      
      // 转换为百分比
      const totalSamples = samples.length
      const percentages = bins.map(count => (count / totalSamples) * 100)
      
      // 构造图表数据
  return this.chartData = binLabels.map((label, index) => [label, percentages[index]])
    },
    renderChart() {
      // 渲染ECharts图表
      if (this.$refs.chartRef && this.chartData.length > 0) {
        this.$refs.chartRef.init(echarts, (chart) => {
          const option = {
            tooltip: {
              trigger: 'axis',
              formatter: (params) => {
                const data = params[0].data;
                return `取款金额: ¥${this.formatCurrency(data[0])}<br/>概率: ${data[1].toFixed(2)}%`;
              }
            },
            xAxis: {
              name: '年取款金额',
              type: 'value',
              axisLabel: {
                formatter: (value) => {
                  return this.formatCurrency(value);
                },
                rotate: 45, // 旋转标签避免重叠
                interval: 0, // 显示所有标签
                fontSize: 10 // 减小字体大小
              }
            },
            yAxis: {
              name: '概率 (%)',
              type: 'value',
              axisLabel: {
                formatter: '{value}%'
              }
            },
            series: [{
              type: 'bar',
              data: this.chartData,
              barWidth: '90%',
              itemStyle: {
                color: '#FF6A39'
              }
            }]
          };
          
          chart.setOption(option);
        });
      }
    },
    formatCurrency(n) {
      const v = Number(n || 0)
      // 使用toFixed确保在所有环境下都保留两位小数，然后添加千位分隔符
      const fixed = v.toFixed(2)
      const parts = fixed.split('.')
      const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
      return integerPart + '.' + parts[1]
     },
     clearForm() {
        const f = this.form
        f.currentAge = 0
        f.retireAge = 0
        f.currentSavings = 0
        f.annualContribution = 0
        f.returnRate = 0
        f.lifespan = 0
        f.considerInflation = false
        f.inflationRate = 0
        f.useMonteCarlo = false
        f.returnStd = 0
        f.simTimes = 10000
        uni.showToast({ title: '已清空表单', icon: 'none' })
      },
      openHistory() {
        uni.navigateTo({ url: '/pages/calculator/history' })
      },
      isInvalid(key) {
        const f = this.form
        const enabled = (key === 'inflationRate') ? !!f.considerInflation
                      : (key === 'returnStd' || key === 'simTimes') ? !!f.useMonteCarlo
                      : true
        if (!enabled) return false
        const val = Number(f[key])
        if (key === 'retireAge') {
          // 仅做范围校验，避免因当前年龄无效导致级联报错
          return isNaN(val) || val < 0 || val > 100
        }
        const limits = {
          currentAge: [0, 100],
          currentSavings: [0, Infinity],
          annualContribution: [0, Infinity],
          returnRate: [0, 50],
          lifespan: [0, 120],
          inflationRate: [0, 20],
          returnStd: [0, 20],
          simTimes: [1000, 100000],
        }
        if (!(key in limits)) return false
        if (isNaN(val)) return true
        const [min, max] = limits[key]
        if (val < min) return true
        if (max !== Infinity && val > max) return true
        return false
      },
      getErrorHint(key) {
        const hints = {
          currentAge: '允许范围：0–100',
          retireAge: '允许范围：0–100',
          currentSavings: '允许范围：≥ 0',
          annualContribution: '允许范围：≥ 0（年末投入）',
          returnRate: '允许范围：0–50%，步长0.1',
          lifespan: '允许范围：0–120',
          inflationRate: '允许范围：0–20%，步长0.1（需开启通胀）',
          returnStd: '允许范围：0–20%，步长0.1（需开启蒙特卡洛）',
          simTimes: '允许范围：1000–100000（需开启蒙特卡洛）',
        }
        return hints[key] || ''
    }
  },
  watch: {
    chartData: {
      handler(newVal) {
        if (newVal.length > 0) {
          this.$nextTick(() => {
            this.renderChart();
          });
        }
      },
      deep: true
    }
  }
}
</script>

<style>
.calc-page { background: linear-gradient(180deg,#E6F7FF 0%, #FFFFFF 100%); min-height: 120vh; }
.calc-page.modal-open { position: fixed; left: 0; right: 0; top: 0; bottom: 0; width: 100%; overflow: hidden; }
.calc-hero { padding: 40rpx 30rpx 20rpx; text-align: center; }
.topbar { display:flex; justify-content: space-between; align-items:center; margin-bottom: 10rpx; }
.back, .help { font-size: 36rpx; color:#111827; padding: 8rpx 18rpx; border-radius: 12rpx; background: rgba(255,255,255,0.6); }
.help { font-weight: 800; }
 .calc-title { font-size: 56rpx; font-weight: 800; color: #111827; text-shadow: 0 2rpx 0 #fff; }
 .calc-sub { margin-top: 16rpx; color: #4B5563; font-size: 28rpx; }
 
.card { background: #FFFFFF; margin: 24rpx 30rpx; padding: 28rpx; border-radius: 24rpx; box-shadow: 0 12rpx 40rpx rgba(0,0,0,0.08); }
.card { background: #FFFFFF; margin: 24rpx 30rpx; padding: 28rpx; border-radius: 24rpx; border: 2rpx solid #E5E7EB; box-shadow: 0 0 0 4rpx #fff, 0 8rpx 0 #cbd5e1, 0 18rpx 32rpx rgba(0,0,0,0.08); }
 .section-title { font-size: 32rpx; font-weight: 700; color: #111827; margin-bottom: 16rpx; }
 .row { display: flex; align-items: center; justify-content: space-between; padding: 14rpx 0; }
 .label { color: #1F2937; font-size: 28rpx; }
 .input { width: 250rpx; height: 68rpx; border-radius: 16rpx; background: #F8FAFC; padding: 0 20rpx; border: 2rpx solid #E5E7EB; }
.input[disabled] { opacity: 0.6; }
.primary { 
  margin: 20rpx 30rpx 40rpx; 
  width: auto;
  height: 80rpx; 
  border-radius: 18rpx; 
  background: linear-gradient(180deg,#FF965B 0%, #FF6A39 100%); 
  color: #fff; 
  font-weight: 700; 
  font-size: 32rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6rpx 0 #E05E2A, 0 14rpx 22rpx rgba(0,0,0,0.12); 
}
.primary:active { transform: translateY(4rpx); box-shadow: 0 2rpx 0 #E05E2A; }
.result .value { font-size: 30rpx; color: #111827; font-weight: 700; }
 .result .actions { display: flex; justify-content: center; margin: 8rpx 0 12rpx; }
 /* 结果弹窗样式 */
 .modal-overlay { position: fixed; left: 0; top: 0; right: 0; bottom: 0; background: rgba(17,24,39,0.4); display: flex; align-items: center; justify-content: center; z-index: 999; }
 .modal { width: 86%; background: #FFFFFF; border: 2rpx solid #E5E7EB; border-radius: 24rpx; box-shadow: 0 0 0 4rpx #fff, 0 8rpx 0 #cbd5e1, 0 18rpx 32rpx rgba(0,0,0,0.12); padding: 24rpx; }
 .modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12rpx; }
 .modal-close { font-size: 36rpx; color: #1F2937; padding: 8rpx 12rpx; }
 .result-modal .actions { display: flex; justify-content: center; margin: 8rpx 0 12rpx; }
 /* 图表容器样式 */
 .chart-container { width: 100%; height: 400rpx; margin-top: 20rpx; }
.secondary { 
  margin: 20rpx 30rpx 80rpx; 
  width: auto;
  height: 80rpx; 
  border-radius: 18rpx; 
  background: linear-gradient(#FFFFFF,#FFFFFF) padding-box, linear-gradient(180deg,#FF965B 0%, #FF6A39 100%) border-box;
  border: 2rpx solid transparent;
  color: #FF6A39; 
  font-weight: 700; 
  font-size: 32rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6rpx 0 #E05E2A, 0 14rpx 22rpx rgba(0,0,0,0.12); 
}
.secondary:active { transform: translateY(4rpx); box-shadow: 0 2rpx 0 #E05E2A; }
 .result .actions { display: flex; justify-content: center; margin: 8rpx 0 12rpx; }
 /* 图表容器样式 */
 .chart-container { width: 100%; height: 400rpx; margin-top: 20rpx; }
.danger {
  margin: 20rpx 30rpx 40rpx;
  width: auto; height: 80rpx; border-radius: 18rpx;
  background: #FFFFFF;
  color: #DC2626; font-weight: 700; font-size: 32rpx;
  display: flex; align-items: center; justify-content: center;
  border: 2rpx solid #FCA5A5;
  box-shadow: 0 6rpx 0 #ffffff, 0 14rpx 22rpx rgba(0,0,0,0.12);
}
.danger:active { transform: translateY(4rpx); box-shadow: 0 2rpx 0 #E5E7EB; }
.actions { display: flex; flex-direction: column; }
 .actions-inline { display: flex; flex-direction: row; justify-content: center; gap: 20rpx; margin: 0 30rpx 80rpx; }
 .actions-inline .danger, .actions-inline .secondary { margin: 0; flex: 1; }
 .input.invalid { border-color: #DC2626; background: #FEE2E2; }
 .hint { display: flex; justify-content: flex-end; padding-top: 6rpx; }
 .error-hint { color: #DC2626; font-size: 24rpx; }
</style>
